package middleware

import (
	"go-gin-init/app/models/resp"
	"go-gin-init/common/result"
	"go-gin-init/common/tool"
	"go-gin-init/common/xerr"
	"time"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

func JWTAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 我们这里jwt鉴权取头部信息 Authorization 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localStorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
		token := c.Request.Header.Get("Authorization")
		if token == "" {
			result.JsonRsp(c, nil, xerr.NewErrCode(xerr.NOT_LOGIN))
			c.Abort()
			return
		}

		j := tool.NewJWT()
		// parseToken 解析token包含的信息
		claims, err := j.ParseToken(token)
		if err != nil {
			// 查看是否过期
			if err == xerr.NewErrCode(xerr.TOKEN_EXPIRED) {
				// 过期，查看过期是否很长时间
				renewToken, err := RenewToken(claims)
				if err != nil {
					result.JsonRsp(c, nil, err)
					c.Abort()
					return
				}
				// 续签成功給返回头设置一个refreshToken字段 ,前端响应拦截器注意及时获取
				c.Header("refreshToken", renewToken)
				// 确保更新的令牌用于后续请求中的身份验证
				c.Request.Header.Set("Authorization", renewToken)
				c.Set("loginUser", claims.BaseClaims)
				c.Next()
				return
			}
			zap.L().Error("gin 中间件", zap.Any("jwt中间件-token解析失败", err))
			result.JsonRsp(c, nil, err)
			c.Abort()
			return
		}

		c.Set("loginUser", claims.BaseClaims)
		c.Next()
	}
}

// RenewToken 续签
func RenewToken(claims *resp.CustomClaims) (string, error) {
	// 若token过期不超过10分钟则给它续签
	if withinLimit(claims.ExpiresAt.Unix(), 600) {
		jwt := tool.NewJWT()
		newClaims := jwt.CreateClaims(claims.BaseClaims)
		token, err := jwt.CreateToken(newClaims)
		if err != nil {
			zap.L().Error("gin 中间件", zap.Any("jwt中间件-jwt续签失败", err))
			return "", xerr.NewErrCode(xerr.TOKEN_FAIL_CREATE)
		}
		return token, nil
	}
	return "", xerr.NewErrCode(xerr.LOGIN_EXPIRETION)
}

// 计算过期时间是否超过l
func withinLimit(s int64, l int64) bool {
	e := time.Now().Unix()
	return e-s < l
}
